package beautiful.butterfly.drds.datasource.connection_contexts.spi.thread;

//import beautiful.butterfly.drds.datasource.author.Author;
//import beautiful.butterfly.drds.datasource.author.Authors;

import beautiful.butterfly.drds.datasource.connection_contexts.spi.ConnectionContexts;
import beautiful.butterfly.drds.datasource.connection_contexts.spi.JdbcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;

//@Author(name = Authors.LI_YANG)
public class CreateConnectionContextTask implements Runnable {
    public static final Logger logger = LoggerFactory.getLogger(CreateConnectionContextTask.class);
    private ConnectionContexts connectionContexts;
    private int exceptionCount = 0;

    public CreateConnectionContextTask(ConnectionContexts connectionContexts) {
        this.connectionContexts = connectionContexts;
    }

    public void run() {
        $();
    }

    private void $() {
        for (; ; ) {

            // addLast
            this.connectionContexts.lock.lock();
            try {
                if (this.connectionContexts.closed) {
                    return;
                }

                boolean needCheck = true;
                if (this.connectionContexts.exceptionWhenCreateConntection != null //
                        &&//
                        this.connectionContexts.poolingIndex == 0) {//
                    needCheck = false;
                }
                if (needCheck) {
                    // 必须存在线程等待，才创建连接
                    if (this.connectionContexts.poolingIndex >= this.connectionContexts.notEmptyWaitThreadCount //
                            && //
                            !(this.connectionContexts.keepAlive && this.connectionContexts.activeCount + this.connectionContexts.poolingIndex < this.connectionContexts.minIdle)) {
                        this.connectionContexts.creatingConnectionTaskCount--;
                        return;
                    }

                    // 防止创建超过maxActive数量的连接
                    if (this.connectionContexts.activeCount + this.connectionContexts.poolingIndex >= this.connectionContexts.maxActive) {
                        this.connectionContexts.creatingConnectionTaskCount--;
                        return;
                    }
                }
            } finally {
                this.connectionContexts.lock.unlock();
            }

            Connection connection = null;
            try {
                connection = this.connectionContexts.createConnection();
                this.connectionContexts.failContinuous.set(false);
            } catch (SQLException e) {
                e.printStackTrace();
                logger.error("create connection error, url: " + this.connectionContexts.jdbcUrl, e);
                this.exceptionCount++;
                if (this.exceptionCount > this.connectionContexts.connectionErrorRetryAttempts//
                        &&//
                        this.connectionContexts.timeMillisBetweenConnectException > 0) {//
                    // fail over retry attempts
                    this.connectionContexts.failContinuous.set(true);
                    //快速失败处理
                    if (this.connectionContexts.failFast) {
                        this.connectionContexts.lock.lock();
                        try {
                            this.connectionContexts.notEmpty.signalAll();
                        } finally {
                            this.connectionContexts.lock.unlock();
                        }
                    }

                    if (this.connectionContexts.breakAfterAcquireFailure) {
                        this.connectionContexts.lock.lock();
                        try {
                            this.connectionContexts.creatingConnectionTaskCount--;//创建+-平衡
                        } finally {
                            this.connectionContexts.lock.unlock();
                        }
                        return;
                    }

                    this.exceptionCount = 0;
                    this.connectionContexts.createConnectionTaskFuture = this.connectionContexts.scheduledExecutorService.schedule(this, connectionContexts.timeMillisBetweenConnectException, TimeUnit.MILLISECONDS);
                    return;
                }

            } catch (Exception e) {
                logger.error("create connection error", e);
                this.connectionContexts.failContinuous.set(true);
                continue;
            } catch (Error e) {
                this.connectionContexts.lock.lock();
                try {
                    this.connectionContexts.creatingConnectionTaskCount--;
                } finally {
                    this.connectionContexts.lock.unlock();
                }
                logger.error("create connection error", e);
                this.connectionContexts.failContinuous.set(true);
                break;
            }

            if (connection == null) {
                continue;
            }

            boolean result = this.connectionContexts.put(connection);
            if (!result) {
                JdbcUtils.close(connection);
                logger.error("put physical connection to pool failed.");
            }
            break;
        }
    }
}
